package org.ykh.gmall.list.service.impl;


import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.util.StringUtils;
import org.ykh.gmall.feign.product.client.ProductFeignClient;
import org.ykh.gmall.list.*;
import org.ykh.gmall.list.repository.GoodsRepository;
import org.ykh.gmall.list.service.SearchService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.ykh.gmall.product.BaseAttrInfo;
import org.ykh.gmall.product.BaseCategoryView;
import org.ykh.gmall.product.BaseTrademark;
import org.ykh.gmall.product.SkuInfo;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@Service
@SuppressWarnings("all")
public class SearchServiceImpl implements SearchService {
    @Autowired
    private GoodsRepository goodsRepository;

    @Autowired
    private ProductFeignClient productFeignClient;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    /**
     * 商品上架
     * @param skuId
     */
    @Override
    public void upperGoods(Long skuId) {

        //创建goods对象
        Goods goods=new Goods();
        //封装数据
        //封装skuInfo
        SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);

        goods.setId(skuInfo.getId());
        goods.setDefaultImg(skuInfo.getSkuDefaultImg());
        goods.setTitle(skuInfo.getSkuName());
        goods.setPrice(skuInfo.getPrice().doubleValue());
        goods.setCreateTime(new Date());
        goods.setTmId(skuInfo.getTmId());

        //封装品牌
        BaseTrademark trademark = productFeignClient.getTrademark(skuInfo.getTmId());
        goods.setTmName(trademark.getTmName());
        goods.setTmLogoUrl(trademark.getLogoUrl());

        //封装三级分类数据
        BaseCategoryView categoryView = productFeignClient.getCategoryView(skuInfo.getCategory3Id());
        goods.setCategory1Id(categoryView.getCategory1Id());
        goods.setCategory1Name(categoryView.getCategory1Name());
        goods.setCategory2Id(categoryView.getCategory2Id());
        goods.setCategory2Name(categoryView.getCategory2Name());
        goods.setCategory3Id(categoryView.getCategory3Id());
        goods.setCategory3Name(categoryView.getCategory3Name());

        //封装平台属性
        List<BaseAttrInfo> attrList = productFeignClient.getAttrList(skuId);


        List<SearchAttr> searchAttrList = attrList.stream().map(baseAttrInfo -> {

            //创建符合搜索的平台属性类型
            SearchAttr searchAttr = new SearchAttr();

            searchAttr.setAttrId(baseAttrInfo.getId());
            searchAttr.setAttrName(baseAttrInfo.getAttrName());

            searchAttr.setAttrValue(baseAttrInfo.getAttrValueList().get(0).getValueName());

            return searchAttr;

        }).collect(Collectors.toList());

        goods.setAttrs(searchAttrList);

        //上传到es
        goodsRepository.save(goods);


    }

    /**
     * 商品的下架
     * @param skuId
     */
    @Override
    public void lowerGoods(Long skuId) {
        goodsRepository.deleteById(skuId);
    }

    /**
     * 更新商品的热度排名
     * @param skuId
     */
    @Override
    public void incrHotScore(Long skuId) {

        //定义热度key
        String incrHotKey="hotScore";
        //自增
        Double aDouble = redisTemplate.opsForZSet().incrementScore(incrHotKey, skuId.toString(), 1);

        //判断
        if(aDouble%10==0){
            //查询指定商品数据
            Optional<Goods> optional = goodsRepository.findById(skuId);

            Goods goods = optional.get();

            //设置热度
            goods.setHotScore(Math.round(aDouble));

            //修改
            goodsRepository.save(goods);

        }
    }


    @Override
    public SearchResponseVo search(SearchParam searchParam) {
        //构建查询请求对象
        SearchRequest searchRequest = buildQueryDsl(searchParam);
        //发送请求
        SearchResponse searchResponse = null;
        try {
            searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        } catch (IOException exception) {
            exception.printStackTrace();
            throw new RuntimeException(exception);
        }
        //封装结果--将es返回的响应对象转换成SearchResponseVo
        SearchResponseVo searchResponseVo =this.resultResponseVo(searchResponse);
        //设置每页条数
        searchResponseVo.setPageSize(searchParam.getPageSize());
        //设置当前页
        searchResponseVo.setPageNo(searchParam.getPageNo());
        //设置总页数
        if(searchResponseVo.getTotal()%searchParam.getPageSize()==0){
            searchResponseVo.setTotalPages(searchResponseVo.getTotal()/searchParam.getPageSize());
        }else{
            searchResponseVo.setTotalPages((searchResponseVo.getTotal()/searchParam.getPageSize())+1);
        }
        return searchResponseVo;
    }

    /**
     *
     * 构建查询请求对象
     * @return
     */
    private SearchRequest buildQueryDsl(SearchParam searchParam) {
        //请求对象
        SearchRequest searchRequest = new SearchRequest("goods");
        //查询构造器--{}
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //多条件构造对象
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //添加关键字查询对象
        //判断关键字
        String keyword = searchParam.getKeyword();
        if(!StringUtils.isEmpty(keyword)){
            boolQuery.must(QueryBuilders.matchQuery("title",keyword).operator(Operator.AND));
        }

        //分类条件添加
        //判断
        if(searchParam.getCategory1Id()!=null){

            boolQuery.filter(QueryBuilders.termQuery("category1Id",searchParam.getCategory1Id()));

        }
        if(searchParam.getCategory2Id()!=null){

            boolQuery.filter(QueryBuilders.termQuery("category2Id",searchParam.getCategory2Id()));

        }
        if(searchParam.getCategory3Id()!=null){

            boolQuery.filter(QueryBuilders.termQuery("category3Id",searchParam.getCategory3Id()));

        }

        //条件品牌对象
        String trademark = searchParam.getTrademark();
        //判断
        if(!StringUtils.isEmpty(trademark)){

            //截取 2:华为    品牌id:品牌名称
            String[] split = trademark.split(":");
            //判断
            if(split!=null &&split.length==2){

                boolQuery.filter(QueryBuilders.termQuery("tmId",split[0]));
            }


        }

        //平台属性封装 props=23:8G:运行内存&props=24:128G:机身内存
        String[] props = searchParam.getProps();
        //判断
        if(props!=null && props.length>0){
            for (String prop : props) {
                //prop=24:128G:机身内存   平台属性id:平台属性值:平台属性名
                //截取
                String[] split = prop.split(":");
                //判断
                if(split!=null &&split.length==3){

                    //拼接dsl
                    //创建多条件封装对象
                    BoolQueryBuilder attrsBoolQuery = QueryBuilders.boolQuery();

                    //创建子多条件对象
                    BoolQueryBuilder subBoolQuery = QueryBuilders.boolQuery();
                    subBoolQuery.must(QueryBuilders.termQuery("attrs.attrId",split[0]));
                    subBoolQuery.must(QueryBuilders.termQuery("attrs.attrValue",split[1]));

                    //关联子查询
                    attrsBoolQuery.must(QueryBuilders.nestedQuery("attrs",subBoolQuery, ScoreMode.None));
                    boolQuery.filter(attrsBoolQuery);

                }

            }


        }

        //设置条件构造器--设置了查询所有的条件
        searchSourceBuilder.query(boolQuery);


        //创建聚合对象--品牌聚合
        TermsAggregationBuilder tmIdAggs = AggregationBuilders.terms("tmIdAggs").field("tmId").size(10);
        tmIdAggs.subAggregation(AggregationBuilders.terms("tmNameAggs").field("tmName").size(10));
        tmIdAggs.subAggregation(AggregationBuilders.terms("tmLogoUrlAggs").field("tmLogoUrl").size(10));

        //设置聚合信息
        searchSourceBuilder.aggregation(tmIdAggs);

        //创建聚合对象-平台属性 --nested

        searchSourceBuilder.aggregation( AggregationBuilders.nested("attrsAggs","attrs")
                .subAggregation(AggregationBuilders.terms("attrIdAggs").field("attrs.attrId").size(10)
                        .subAggregation(AggregationBuilders.terms("attrNameAggs").field("attrs.attrName").size(10))
                        .subAggregation(AggregationBuilders.terms("attrValueAggs").field("attrs.attrValue").size(10))));


        //设置排序
        //判断用户提交的排序方式
        String order = searchParam.getOrder();
        //判断
        if(!StringUtils.isEmpty(order)){
            //order:      1:desc   2:desc     1是热度排序  2是价格排序
            //截取
            String[] split = order.split(":");
            //判断
            if(split!=null &&split.length==2){

                //定义介绍字段
                String field="";
                switch (split[0]){

                    case "1":
                        field="hotScore";
                        break;
                    case "2":
                        field="price";
                        break;

                }


                //设置排序
                searchSourceBuilder.sort(field,"desc".equals(split[1])? SortOrder.DESC:SortOrder.ASC);

            }



        }else {

            //默认排序方式 热度排序
            searchSourceBuilder.sort("hotScore", SortOrder.DESC);
        }

        //设置高亮
        HighlightBuilder builder=new HighlightBuilder();
        //设置前缀
        builder.preTags("<font color='red'>");
        //设置后缀
        builder.postTags("</font>");
        //设置字段
        builder.field("title");
        searchSourceBuilder.highlighter(builder);



        //设置分页
        //开始索引
        searchSourceBuilder.from((searchParam.getPageNo()-1)*searchParam.getPageSize());
        //每页条数
        searchSourceBuilder.size(searchParam.getPageSize());
        //结果集过滤
        searchSourceBuilder.fetchSource(new String[]{ "id","title","defaultImg","price"},null);
        //检验一下dsl的整体
        System.out.println("封装 的Dsl:\t"+searchSourceBuilder.toString());
        //查询构造器添加到请求对象
        searchRequest.source(searchSourceBuilder);
        return searchRequest;
    }

    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 结果集转化
     * @param searchResponse
     * @return
     */
    private SearchResponseVo resultResponseVo(SearchResponse searchResponse) {
        //searchResponse:表示查询后的结果--json对象
        SearchResponseVo searchResponseVo=new SearchResponseVo();
        //获取所有聚合
        Map<String, Aggregation> aggregationMap = searchResponse.getAggregations().asMap();
        //获取品牌聚合信息
        ParsedLongTerms tmIdAggs = (ParsedLongTerms) aggregationMap.get("tmIdAggs");
        //获取数据
        List<SearchResponseTmVo> trademarkList = tmIdAggs.getBuckets().stream().map(bucket -> {
            //创建品牌封装对象
            SearchResponseTmVo tmVo = new SearchResponseTmVo();

            //获取品牌id
            long tmId = ((Terms.Bucket) bucket).getKeyAsNumber().longValue();
            //获取品牌名称聚合对象
            Map<String, Aggregation> subAggretion = ((Terms.Bucket) bucket).getAggregations().asMap();

            ParsedStringTerms tmNameAggs = (ParsedStringTerms) subAggretion.get("tmNameAggs");
            String tmName = tmNameAggs.getBuckets().get(0).getKeyAsString();
            //获取品牌logoUrl的值
            ParsedStringTerms tmLogoUrlAggs = (ParsedStringTerms) subAggretion.get("tmLogoUrlAggs");
            String tmLogoUrl = tmLogoUrlAggs.getBuckets().get(0).getKeyAsString();

            //封装数据
            tmVo.setTmId(tmId);
            tmVo.setTmName(tmName);
            tmVo.setTmLogoUrl(tmLogoUrl);
            return tmVo;
        }).collect(Collectors.toList());
        //设置品牌集合到响应对象
        searchResponseVo.setTrademarkList(trademarkList);


        //获取平台属性聚合对象
        ParsedNested attrsAggs = (ParsedNested) aggregationMap.get("attrsAggs");
        ParsedLongTerms attrIdAggs = attrsAggs.getAggregations().get("attrIdAggs");
        //获取数据数组
        List<SearchResponseAttrVo> attrsList = attrIdAggs.getBuckets().stream().map(bucket -> {
            //创建封装对象
            SearchResponseAttrVo responseAttrVo = new SearchResponseAttrVo();
            //获取平台属性id
            long attrId = ((Terms.Bucket) bucket).getKeyAsNumber().longValue();
            //设置id
            responseAttrVo.setAttrId(attrId);
            //获取平台属性所有子聚合
            Map<String, Aggregation> subAggregation = bucket.getAggregations().asMap();

            //获取平台属名聚合对象
            ParsedStringTerms attrNameAggs = (ParsedStringTerms) subAggregation.get("attrNameAggs");
            String attrName = attrNameAggs.getBuckets().get(0).getKeyAsString();
            responseAttrVo.setAttrName(attrName);

            //获取平台属性值聚合对象
            ParsedStringTerms attrValueAggs = (ParsedStringTerms) subAggregation.get("attrValueAggs");
            //获取平台属性值集合
            List<String> attrValueList = attrValueAggs.getBuckets().stream().map(subBucket -> {
                //获取平台属性值
                return ((Terms.Bucket) subBucket).getKeyAsString();
            }).collect(Collectors.toList());
            //设置平台属性值集合
            responseAttrVo.setAttrValueList(attrValueList);

            return responseAttrVo;
        }).collect(Collectors.toList());

        //设置平台属性集合到响应对象
        searchResponseVo.setAttrsList(attrsList);

        //获取数据内容对象
        SearchHits hits = searchResponse.getHits();
        //获取商品列表
        SearchHit[] sourceHits = hits.getHits();
        //判断
        if(sourceHits!=null&&sourceHits.length>0){

            List<Goods> goodsList = Arrays.stream(sourceHits).map(searchHit -> {
                //获取goods的json字符串
                String goodsStr = searchHit.getSourceAsString();
                //转换成对象
                final Goods goods;
                try {
                    goods = objectMapper.readValue(goodsStr, Goods.class);
                } catch (JsonProcessingException e) {
                    e.printStackTrace();
                    throw new RuntimeException(e);
                }
//                Goods goods = JSONObject.parseObject(goodsStr, Goods.class);

                //处理高亮字段
                if(searchHit.getHighlightFields().get("title")!=null){

                    String title = searchHit.getHighlightFields().get("title").getFragments()[0].toString();
                    goods.setTitle(title);
                }
                return goods;
            }).collect(Collectors.toList());

            //封装商品列表
            searchResponseVo.setGoodsList(goodsList);

        }

        //封装分页数据
        //获取总记录数据
        long total = hits.getTotalHits().value;
        searchResponseVo.setTotal(total);

        return searchResponseVo;
    }
}
